/**
 * @file jisp.cpp
 * @brief  ISP类，通过ISP协议，访问单片机信息
 *
 * Copyright (C) 2014 JakeYang <add358@gmail.com>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */
#include "jisp.h"
#include <QDebug>
#include <QThread>

/* STM32 ISP */
#define ISP_AUTO_BAUDRATE	0x7F
#define ISP_ACK			0x79
#define ISP_NACK		0x1F
#define ISP_GET_COMMAND		0x00
#define ISP_GET_VERSION		0x01
#define ISP_GET_ID		0x02
#define ISP_READ_MEMORY		0x11
#define ISP_GO			0x21
#define ISP_WRITE_MEMORY	0x31
#define ISP_ERASE		0x43
#define ISP_WRITE_PROTECT	0x63
#define ISP_WRITE_UNPROTECT	0x73
#define ISP_READOUT_PROTECT	0x82
#define ISP_READOUT_UNPROTECT	0x92

/* 定义命令 */
static const char ISP_AUTO_BAUDRATE_CMD[] = {ISP_AUTO_BAUDRATE};

#define BUF_MAX 255         //缓冲区最大值
#define TRY_TIMES	3           //尝试读取次数
#define WAIT_INTERVAL	600    //等待延迟时间，ms

void Jisp::flash()
{
    QString msg;
    char buf[BUF_MAX] = {0};

    setReadBufferSize(2048);
    qDebug()<<"read buf size"<<readBufferSize();

    msg.clear();
    if ( !(isOpen()
           && auto_baud()
           && get_version(msg)
           && get_id(msg)
           && get_command(msg))) {
        return;
    }
    emit send_exec_msg(GET_COMMAND, msg);

    QByteArray byteArray;
    int try_time = 5;
    while (try_time--) {
        if ( read_memory(0x8000000, 20, byteArray) ) {
            break;
        }
    }
    msg = byteArray.toHex();
    emit send_exec_msg(GET_COMMAND, msg);

    emit send_flash_progress(20);
}

/**
 * @brief Jisp::auto_baud  发送标志字节，让单片机配置主机端的串口速率
 * @return true   成功
 *               false 失败
 */
bool Jisp::auto_baud()
{
    bool ret = true;
    if ( !send_cmd(ISP_AUTO_BAUDRATE) ) {
        ret = false;
        emit send_exec_msg(EXEC_ERROR, "配对目标机波特率失败\n请确认：\n1 目标板设置正确的启动模块\n2 目标板串口支持本软件设置的速率\n3 重启目标板");
    }
    return ret;
}

/**
 * @brief Jisp::get_command     获取ISP支持命令
 * @param msg   保存获取信息
 * @return  获取成功为true，否则为false
 */
bool Jisp::get_command(QString &msg)
{
    QByteArray ret_bytes;
    QByteArray::const_iterator i_bytes;
    int i;
    if ( ! send_cmd(ISP_GET_COMMAND) ) {
        goto GET_COMMAND_FAILED;
    }
    read_data(ret_bytes);
    if ( ret_bytes.count() < 0 ) {
        goto GET_COMMAND_FAILED;
    }

    ret_bytes.remove(0,2);    /* 去掉表示数量字节和版本号字节 */

    msg.append("\nSupport ISP Commands：");
    for (i_bytes = ret_bytes.constBegin(),i=1; i_bytes != ret_bytes.constEnd(); ++i_bytes,++i) {
        switch ((quint8)*i_bytes) {
        case ISP_GET_COMMAND:   msg.append("\n\t"+QString::number(i)+": Get Command(0x");
            break;
        case ISP_GET_VERSION:    msg.append("\n\t"+QString::number(i)+": Get Version and Read Protection Status(0x");
            break;
        case ISP_GET_ID:    msg.append("\n\t"+QString::number(i)+": Get ID(0x");
            break;
        case ISP_READ_MEMORY:   msg.append("\n\t"+QString::number(i)+": Read Memory Command(0x");
            break;
        case ISP_GO:    msg.append("\n\t"+QString::number(i)+": Go Command(0x");
            break;
        case ISP_WRITE_MEMORY:  msg.append("\n\t"+QString::number(i)+": Write Memory Command(0x");
            break;
        case ISP_ERASE: msg.append("\n\t"+QString::number(i)+": Erase Command(0x");
            break;
        case ISP_WRITE_PROTECT: msg.append("\n\t"+QString::number(i)+": Write Protect Command(0x");
            break;
        case ISP_WRITE_UNPROTECT:   msg.append("\n\t"+QString::number(i)+": Write Unprotect Command(0x");
            break;
        case ISP_READOUT_PROTECT:   msg.append("\n\t"+QString::number(i)+": Readout Protect Command(0x");
            break;
        case ISP_READOUT_UNPROTECT: msg.append("\n\t"+QString::number(i)+": Readout Unprotect Command(0x");
            break;
        default:    msg.append("\n\t"+QString::number(i)+": Unkown Command(0x");
            break;
        }
        msg.append(QString::number((quint8)*i_bytes,16)+")");
    }
    return true;
GET_COMMAND_FAILED:
    emit send_exec_msg(EXEC_ERROR, "获取单片机的ISP所有命令失败！");
    return false;
}

bool Jisp::get_version(QString &msg)
{
    QByteArray byteArray;
    char ver;
    if ( !send_cmd(ISP_GET_VERSION) ) {
        goto GET_VERSION_FAILED;
    }

    read_data(byteArray);
    if ( byteArray.count() < 1 ) {
        goto GET_VERSION_FAILED;
    }

    ver = byteArray.at(0);
    msg.append("\nBootload Version : "+QString::number(ver/16, 16)+"."+QString::number(ver%16,16));

    return true;

GET_VERSION_FAILED:
    emit send_exec_msg(EXEC_ERROR, "获取版本号和状态失败");
    return false;
}

bool Jisp::get_id(QString &msg)
{
    QByteArray byteArray;

    if ( ! send_cmd(ISP_GET_ID) ) {
        goto GET_ID_FAILED;
    }

    read_data(byteArray);
    if ( byteArray.count() < 3 ) {
        goto GET_ID_FAILED;
    }

    byteArray.remove(0,1);

    msg.append("\nProduct ID : 0x"+byteArray.toHex());
    return true;

GET_ID_FAILED:
    emit send_exec_msg(EXEC_ERROR, "获取ID失败");
    return false;
}

void Jisp::display(const char *buf, int len)
{
    QString msg;
    for( int i = 0; i< len; ++i) {
        msg.append(" 0x"+QString::number((quint8)buf[i],16));
        if( 0!=i && 0 == i%15 ) {
            msg.append("\n");
        }
    }
    qDebug()<<msg;
}

/**
 * @brief Jisp::read_memory  读取存储器的数据
 * @param addr  要读取数据的起始地址
 * @param len    要读取数据的长度
 * @param byteArray  保存读取数据
 * @return         成功返回true， 失败返回false
 */
bool Jisp::read_memory(int addr, int len, QByteArray &byteArray)
{
    if ( !( send_cmd(ISP_READ_MEMORY)
            && send_addr(addr)
            && send_number(len))) {
        return false;
    }

    read_data(byteArray);
    qDebug()<<byteArray.toHex();
    return byteArray.count() == len;
}

/**
 * @brief Jisp::checksum  计算检验和
 * @param buf   要检验的数据
 * @return        返回检验值
 */
char Jisp::checksum(const QByteArray &byteArray)
{
    char sum = 0;
    for (QByteArray::const_iterator iterator = byteArray.begin();
         iterator != byteArray.end();
         ++iterator) {
        sum ^= *iterator;
    }
    return sum;
}

#define SEND_CMD_BUF_LEN 2
/**
 * @brief Jisp::send_cmd 发送要操作的命令
 * @param cmd   要发送的命令
 * @return      发送成功返回true，否则返回false
 */
bool Jisp::send_cmd(char cmd)
{
    char ret[1]={0xff};
    char cmd_buf[SEND_CMD_BUF_LEN] = {cmd, (char)~cmd};
    qDebug()<<"Exec "<<QString::number(cmd,16);

    flush();
    clear();
    if ( ISP_AUTO_BAUDRATE == cmd ) {
        write(cmd_buf, 1);
    } else {
        write(cmd_buf, SEND_CMD_BUF_LEN);
    }

    while (1) {
        if ( !waitForReadyRead(WAIT_INTERVAL) ) {
            qDebug()<<__func__<<"timeout";
            goto SEND_CMD_FAILED;
        }
        readData(ret, 1);
        if ( ISP_ACK != ret[0] ) {
            qDebug()<<__func__<<"ISP_NACK";
            continue;
        }
        break;
    }
    return true;

SEND_CMD_FAILED:
    return false;
}

/**
 * @brief Jisp::send_addr 发送要操作的地址
 * @param addr  要发送的地址
 * @return         发送成功返回true，否则返回false
 */
bool Jisp::send_addr(int addr)
{
    QByteArray byteArray;
    addr = htobe32(addr);
    byteArray.append((char *)&addr,4);
    byteArray += checksum(byteArray);

    flush();
    clear();
    if ( !waitForReadyRead(WAIT_INTERVAL) ) {
        qDebug()<<__func__<<" timeout";
        goto SEND_ADDR_FAILED;
    }
    byteArray.clear();
    byteArray = readAll();
    if ( byteArray.indexOf(ISP_ACK) < 0) {
        qDebug()<<"send addr failed";
        goto SEND_ADDR_FAILED;
    }

    return true;

SEND_ADDR_FAILED:
    qDebug()<<"send addr failed";
    return false;
}

/**
 * @brief Jisp::read_data  读取ISP数据，不包括ACK、NACK
 * @param bytes      保存读取到的数据
 */
void Jisp::read_data(QByteArray &byteArray)
{
    while(1) {
        if ( !waitForReadyRead(WAIT_INTERVAL) ) {
            qDebug()<<__func__<<"timeout";
            break;
        }
        byteArray.append(read(1));
        if (ISP_ACK == *byteArray.end() ) {
            break;
        }
    }
    int index = -1;
    while (1) {
        index = byteArray.indexOf(ISP_ACK);
        if ( index < 0 ) {
            break;
        }
        byteArray.remove(index,1);
    }
    qDebug()<<byteArray.toHex();
}

/**
 * @brief Jisp::send_number 发送要接收或发送的字节数
 * @param num           字节数
 * @return              成功返回true；否则返回false
 */
bool Jisp::send_number(char num)
{
#define NUMBER_BUF_LEN 2
    char number_buf[NUMBER_BUF_LEN] = {0};

    number_buf[0] = num;
    number_buf[1] = ~number_buf[0];

    flush();
    clear();
    write(number_buf, NUMBER_BUF_LEN);
    if ( !waitForReadyRead(WAIT_INTERVAL) ) {
        qDebug()<<__func__<<"timeout";
        goto SEND_NUMBER_FAILED;
    }
    read(number_buf, 1);
    if (ISP_ACK != number_buf[0]) {
        goto SEND_NUMBER_FAILED;
    }

    return true;

SEND_NUMBER_FAILED:
    return false;
}
